package model;

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import model.semantics.AbstractToken;


/**
 * Evaluate an expression for each pixel in a image.
 * 
 * @author Robert C Duvall
 * @author Troy Ferrell(modified)
 */
public class Model
{
    public static final double DOMAIN_MIN = -1;
    public static final double DOMAIN_MAX = 1;
    
    private PicassaTreeBuilder myTreeBuilder;

    public Map<String, String> myVariables = new HashMap<String, String>();
    public ArrayList<String> myExpressions = new ArrayList<String>();
    public Pixmap currentImage = new Pixmap();
    
    private static final String TOKENIZER_DELIMITER = "\\s+"; 
    private static final String EQUALS_STR = "=";
    
    /**
     * Create a basic model of the system.
     */
    public Model ()
    {
        myTreeBuilder = new PicassaTreeBuilder();
    }
    
    /**
     * Evaluate the expression.
     * @param infix
     * @param size
     */
    public void evaluate (String infix, Dimension size)
    {
    	if(infix.contains(EQUALS_STR)) // should be variable
    		evaluateVariable(infix);
    	else
    		evaluateImage(infix, size);
    }
    
    /**
     * Evaluates variables when enetered.
     * @param expression
     */
    private void evaluateVariable(String expression)
    {
    	String [] tokenized = expression.split(TOKENIZER_DELIMITER);
    	if(!tokenized[1].equals(EQUALS_STR))
    	{
    		NotifyPicassa.showError("Improperly placed equals sign.");
    		return;
    	}
    	String varName = tokenized[0];
    	String varExpression = expression.substring( expression.indexOf(EQUALS_STR) + 1 );
    	
    	if( createExpressionTree(varExpression) != null)
    	{
    		myVariables.put(varName, varExpression);
    	}
    	else
    		NotifyPicassa.showError("Null expression.");
    }

    public void evaluateImage(String infix, Dimension size)
    {
    	AbstractToken toEval = createExpressionTree(infix);
        Pixmap result = new Pixmap(size);

        for (int imageY = 0; imageY < size.height; imageY++)
        {
            RGBColor colorY = new RGBColor( imageToDomainScale(imageY, size.height) );
            
            for (int imageX = 0; imageX < size.width; imageX++)
            {
                RGBColor colorX = new RGBColor( imageToDomainScale(imageX, size.width) );
               result.setColor(imageX, imageY, toEval.evaluate(colorX, colorY).toJavaColor());
            }
        }
        
        myExpressions.add(infix);
        currentImage = result;
    }
    

    /**
     * Convert from image space to domain space.
     */
    protected double imageToDomainScale (int value, int bounds)
    {
        double range = DOMAIN_MAX - DOMAIN_MIN;
        return ((double)value / bounds) * range + DOMAIN_MIN;
    }
    
    /**
     * A place holder for more interesting way to build the expression.
     */
    private AbstractToken createExpressionTree (String infixString)
    {
		return myTreeBuilder.buildTree( processExpressionForVariables(infixString) );
    }
    
    private String processExpressionForVariables(String expression)
    {
    	String [] tokenized = expression.split(TOKENIZER_DELIMITER);
    	StringBuilder returnString = new StringBuilder();
    	
    	for(int i = 0; i < tokenized.length; i++)
    	{
    		if(myVariables.keySet().contains(tokenized[i]))
    		{
    			tokenized[i] = myVariables.get(tokenized[i]);
    		}
    		
    		returnString.append(tokenized[i] + " ");
    	}

    	return returnString.toString();
    }
    
    private String processExpressionforHistory(String expression)
    {
    	// TODO: need to write preprocessing code to insert expressions
    	//	     from history for the indexes in current expression(e.g $1 for the first enetered expression)
    	
    	return null;
    }
    
}
